home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Publishing
/
ImagePortfolio
/
Source
/
PaletteCell.m
< prev
next >
Wrap
Text File
|
1994-04-01
|
12KB
|
448 lines
// -------------------------------------------------------------------------------------
// PaletteCell.m - image cell
// Martin D. Flynn, NeXT Computer, Inc.
// You may freely copy, distribute and reuse the code in this example.
// NeXT disclaims any warranty of any kind, expressed or implied, as to its
// fitness for any particular use.
// -------------------------------------------------------------------------------------
#import "PaletteCell.h"
#import <stdlib.h>
#import <stdio.h>
#import <string.h>
#import <stdarg.h>
#import <math.h>
#import <objc/List.h>
#import <appkit/Cursor.h>
#import <appkit/View.h>
#import <appkit/Text.h>
#import <appkit/NXImage.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/nextstd.h>
#import <dpsclient/wraps.h>
#import "PaletteMatrix.h"
#import "ImagePortfolio.h"
// -------------------------------------------------------------------------------------
// delegate selectors
#define becameSELECTED @selector(cellDidBecomeSelected:)
#define resignSELECTED @selector(cellDidResignSelected:)
// -------------------------------------------------------------------------------------
#define CELLOFFSET ((cFlags1.bordered || cFlags1.bezeled)?(cFlags1.bordered?2.0:3.0):0.0)
#define HIGHLIGHTED ((BOOL)(cFlags1.highlighted || cFlags1.state))
// -------------------------------------------------------------------------------------
@implementation PaletteCell
// -------------------------------------------------------------------------------------
// support for multiple image types
#define maxFILETYPES 32
static char *imageFileExtn[maxFILETYPES + 1];
static List *imageClassList = (List*)nil;
static int imageClassCount = 0;
/* add image class type */
+ addImageClass:classId, ...
{
int i;
char *cp;
va_list args;
/* initialize */
if (!imageClassList) {
imageClassList = [[[List alloc] initCount:1] empty];
memset(imageFileExtn, 0, sizeof(imageFileExtn));
}
/* add class object */
[imageClassList addObject:classId];
imageClassCount = [imageClassList count];
/* add valid extensions */
for (i = 0; imageFileExtn[i]; i++); // find end of list
va_start(args, classId);
for (;;) if (cp=va_arg(args,char*)) imageFileExtn[i++]=NXCopyStringBuffer(cp); else break;
va_end(args);
return self;
}
/* return true if extension is valid */
BOOL _validExtension(char *fileName)
{
int i;
char *extn = (char*)fileEXT((char*)fileName);
for (i = 0; imageFileExtn[i]; i++) if (!strcasecmp(extn, imageFileExtn[i])) return YES;
return NO;
}
// -------------------------------------------------------------------------------------
// new cell methods
/* old generic new cell */
+ new
{
return [self newIconCell];
}
/* generic new cell */
+ newIconCell
{
self = [self newIconCell:(char*)nil];
[titleCell setStringValueNoCopy:""];
return self;
}
/* new cell with title */
+ newIconCell:(const char *)aString
{
return [[self allocFromZone:NXDefaultMallocZone()] initIconCell:aString];
}
/* generic init */
- init
{
[self initIconCell:(char*)nil];
[titleCell setStringValueNoCopy:""];
return self;
}
/* init with title */
- initIconCell:(const char *)aString
{
/* init instance */
[super initIconCell:(char*)nil];
[self setStringValueNoCopy:""];
[self setAlignment:NX_CENTERED];
titleCell = [[Cell allocFromZone:[self zone]] initTextCell:aString];
[titleCell setAlignment:NX_CENTERED];
[self setBezeled:NO];
/* init vars */
cFlags1.editable = NO;
cFlags1.selectable = NO;
cFlags2.noWrap = YES;
cFlags2._isLeaf = YES;
delegate = self;
imageId = (id)nil;
smallImage = (id)nil;
imagePath = (char*)nil;
imageMutex = mutex_alloc();
return self;
}
/* make an empty copy of this cell */
- copy
{
NXZone *zone = [self zone];
id tCell = titleCell;
self = [super copyFromZone:zone];
titleCell = [[tCell copyFromZone:zone] setFont:[tCell font]];
imageMutex = mutex_alloc();
imageId = (id)nil;
smallImage = (id)nil;
imagePath = (char*)nil;
return self;
}
/* free this cell */
- free
{
if (imagePath) free(imagePath);
if (imageId) [imageId free];
if (smallImage) [smallImage free];
[titleCell free];
mutex_free(imageMutex);
return [super free];
}
/* set matrix owner id */
- setDelegate:anObject
{
delegate = anObject;
return self;
}
// -------------------------------------------------------------------------------------
// set cell size
- setCellSize:(const NXSize*)cellSize
{
if (smallImage) { [smallImage free]; smallImage = (id)nil; }
return self;
}
- setFont:fontId
{
return [titleCell setFont:fontId];
}
// -------------------------------------------------------------------------------------
// image methods
/* load image specified in preset file name */
- loadImageFile
{
int i;
id tempId = (id)nil;
/* validate load */
if (!imagePath) return (id)nil; // no file specified
if (imageId) return imageId; // already loaded
/* try all image classes until we find one we like */
for (i = 0; !tempId && (i < imageClassCount); i++) {
int j;
NXSize size;
id repList = [[imageClassList objectAt:i] newListFromFile:imagePath];
if (!repList) continue;
if (![repList count]) { [repList free]; continue; }
[[repList objectAt:0] getSize:&size];
if (!size.width || !size.height) { [repList freeObjects]; [repList free]; continue; }
tempId = [[NXImage alloc] init];
for (j = 0; j < [repList count]; j++) [tempId useRepresentation:[repList objectAt:j]];
[repList free];
mutex_lock(imageMutex);
imageId = tempId;
mutex_unlock(imageMutex);
break;
}
return tempId;
}
/* open image (may be called from non-main thread) */
- setImageFile:(const char *)filePath
{
char *iName;
/* free prior storage (actually this should never be necessary!) */
mutex_lock(imageMutex);
if (imagePath ) { free(imagePath); imagePath = (char*)nil; }
if (imageId ) { [imageId free]; imageId = (id)nil; }
if (smallImage) { [smallImage free]; smallImage = (id)nil; }
mutex_unlock(imageMutex);
/* save filename and title */
imagePath = NXCopyStringBuffer(filePath);
iName = fileNAME((char*)filePath);
[titleCell setStringValue:(const char*)iName]; // OK since it can't draw yet
free(iName);
/* open new image and make sure it was readable */
return ([self mainThreadPerform:@selector(loadImageFile) wait:YES])?self:(id)nil;
}
/* return image id */
- image
{
id image;
if (!imagePath) return (id)nil;
mutex_lock(imageMutex);
image = imageId;
mutex_unlock(imageMutex);
return image;
}
/* return image representation */
- imageRepresentation:(int)imageNum
{
id repList, image = [self image];
if (!image) return (id)nil;
repList = [image representationList];
return [repList objectAt:(imageNum % [repList count])];
}
/* return path to image */
- (const char*)imagePath
{
return imagePath;
}
/* return cell title */
- (const char*)cellTitle
{
return [titleCell stringValue];
}
// -------------------------------------------------------------------------------------
- calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
{
NXRect rect = *aRect;
NXSize titleSize;
theSize->width = 1.0;
theSize->height = 1.0;
[titleCell calcCellSize:&titleSize inRect:&rect];
theSize->width += titleSize.width;
theSize->height = MAX(titleSize.height, theSize->height);
return self;
}
// -------------------------------------------------------------------------------------
// highlight cell
/* set selected state */
- setState:(int)flag
{
if (![self image]) return self; // ignore if no image in cell
if ((!cFlags1.state && flag) || (cFlags1.state && !flag)) {
cFlags1.state = flag? YES : NO;
if (flag) {
if (delegate && [delegate respondsTo:@selector(cellBecameSelected:)])
[delegate cellBecameSelected:self];
} else {
if (delegate && [delegate respondsTo:@selector(cellResignedSelected:)])
[delegate cellResignedSelected:self];
}
}
return self;
}
/* highlight and select */
- highlight:(const NXRect*)cellFrame inView:controlView lit:(BOOL)flag
{
if (![self image]) return self; // ignore if no image in cell
if ((!cFlags1.highlighted && flag) || (cFlags1.highlighted && !flag)) {
cFlags1.highlighted = flag? YES : NO;
[self setState:cFlags1.highlighted];
[self drawSelf:cellFrame inView:controlView];
}
return self;
}
/* return highlight state */
- (BOOL)isHighlighted
{
return HIGHLIGHTED;
}
/* return selected state */
- (BOOL)isSelected
{
return (BOOL)cFlags1.state;
}
// -------------------------------------------------------------------------------------
// tool methods
/* resize image (only if newSize is smaller than original) (MAIN THREAD ONLY!) */
- _resizeImage:image:(int)num toSize:(NXSize*)toSize
{
float aspect;
NXRect r = { { 0.0, 0.0 }, { 0.0, 0.0 } };
id newImage;
/* return if already within bounds */
if (!image) return (id)nil;
[image getSize:&r.size];
if ((r.size.width <= toSize->width) && (r.size.height <= toSize->height)) return (id)nil;
/* calculate new bounds */
aspect = r.size.width / r.size.height;
if (r.size.width > toSize->width ) r.size.width = toSize->width ;
if (r.size.height > toSize->height) r.size.height = toSize->height;
if (r.size.width > r.size.height * aspect) r.size.width = r.size.height * aspect;
if (r.size.height > r.size.width / aspect) r.size.height = r.size.width / aspect;
/* create a new image */
newImage = [[NXImage alloc] initSize:&r.size];
if (!newImage) {
NXLogError("ImagePortfolio: Unable to init scaled image");
return (id)nil;
}
[newImage setFlipped:[image isFlipped]];
/* draw scaled image */
PSgsave();
if ([newImage lockFocus]) {
[[self imageRepresentation:num] drawIn:&r];
[newImage unlockFocus];
} else {
[newImage free];
newImage = (id)nil;
}
PSgrestore();
/* return resize image */
return newImage;
}
// -------------------------------------------------------------------------------------
// smallImage support
/* calculate bounding size for small image */
- calcImageSize:(NXSize*)imageSize forCellSize:(NXSize*)cellSize
{
NXSize tSize;
NXRect cFrame = { {0.0, 0.0}, {0.0, 0.0} };
cFrame.size = *cellSize;
NXInsetRect(&cFrame, 1.0, 1.0);
[titleCell calcCellSize:&tSize inRect:&cFrame];
cFrame.size.height -= tSize.height + 2.0;
*imageSize = cFrame.size;
return self;
}
// -------------------------------------------------------------------------------------
// draw methods
- drawSelf:(const NXRect *)cellFrame inView:controlView
{
return [self drawInside:cellFrame inView:controlView];
}
- drawInside:(const NXRect *)cellFrame inView:controlView
{
NXSize tSize = {0.0, 0.0}, iSize;
NXRect rect, cFrame = *cellFrame;
float offset = CELLOFFSET;
BOOL multi = NO;
id image;
/* draw border */
if (cFlags1.bordered) { PSsetgray(NX_BLACK); NXFrameRectWithWidth(cellFrame, 1.0); } else
if (cFlags1.bezeled ) { NXDrawGrayBezel(cellFrame, (NXRect*)nil); }
/* inset rectangle and draw highlight */
NXInsetRect(&cFrame, offset, offset);
PSsetgray((HIGHLIGHTED)? NX_WHITE: NX_LTGRAY);
NXRectFill(&cFrame);
NXInsetRect(&cFrame, 1.0, 1.0);
/* calc text size */
[titleCell calcCellSize:&tSize inRect:&cFrame];
/* resize & draw image */
if ([self image]) {
rect = cFrame;
rect.size.height -= tSize.height + 2.0;
rect.origin.y += rect.size.height;
if ([[imageId representationList] count] > 1) multi = YES;
if (!smallImage) smallImage = [self _resizeImage:imageId:0 toSize:&rect.size];
image = (smallImage)? smallImage : imageId;
[image getSize:&iSize];
rect.origin.y -= (rect.size.height - iSize.height) / 2.0;
rect.origin.x += (rect.size.width - iSize.width ) / 2.0;
[image composite:NX_SOVER toPoint:&rect.origin];
}
/* draw image title cell */
rect = cFrame;
rect.origin.y += rect.size.height - tSize.height - 1;
rect.origin.x += floor((rect.size.width - tSize.width) / 2.0);
rect.size.width = tSize.width;
rect.size.height = tSize.height;
[titleCell drawSelf:&rect inView:controlView];
return self;
}
@end